#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>

#include "always_assert.h"
// TODO hsq 作为共享库，不应该依赖 internal.h ？但是 khash 中的宏依赖它。
#include "internal.h"
#include "potion.h"
#include "khash.h"
#include "table.h"

#include "linenoise.h"

// TODO hsq 可配置文件位置、最大数？
static char history_file[] = "/tmp/potion_repl.txt";
static uint16_t history_max = 64;

// TODO hsq linenoise 是进程级的，进程内不可多实例； hints 加个指针参数比较方便。
Potion *P = NULL;

static char *hints(const char *buf, int *color, int *bold) {
    vPN(Vtable) vt = NULL;
    // if (buf[0] == 9 && buf[1] == '\0') { // ""
    if (strcasecmp("Lobby", buf) == 0) {
        vt = (struct PNVtable *)P->lobby;
        // PN str = potion_send(vt->methods, $(string));
        unsigned i = 0;
        // TODO hsq hints 显示也应该是多行的？下面注释中的代码产生怪异的缩进。
        // unsigned line_num = 1;
        PN bytes = potion_bytes_str(P, " ");
        KH_EACH_ASC(PN, vt->methods, {
            if (i++ > 0) POTION_BYTES_LITERAL(bytes, ", ");
            // if (PN_STR_LEN(bytes) > line_num * 80) {
            //     line_num++;
            //     POTION_BYTES_LITERAL(bytes, "\n");
            // }
            potion_bytes_obj_string(P, bytes, kh_key(PN, t, k));
        });
        *color = 35;
        *bold = 0;
        return PN_STR_PTR(bytes);
    } else if (buf[0] == '@' && buf[1] == '\0') {
        // TODO hsq
    }
    return NULL;
}

#undef P // }}}

PN_MSG(potion_readline, PN prompt);
// static void (^potion_readline_save)(void);
// static void potion_readline_save(void);

PN potion_lineedit_init(Potion *_P) {
    P = _P;

    linenoiseSetMultiLine(1);
    // TODO hsq 1:成功， 0:失败；以及下面的 linenoiseHistoryAdd ；统一返回值风格？
    assert(1 == linenoiseHistorySetMaxLen(history_max));
    // TODO hsq 其他配置，如提示、补全 ...
    linenoiseSetHintsCallback(hints);

    PN_METHOD(P->lobby, "readline", potion_readline, "prompt=S");

    // 0:成功， -1:失败(文件不存在)。
    int err = linenoiseHistoryLoad(history_file);
    if (err && err != -1) {
        FAIL_MSG("linenoiseHistoryLoad() return %d", err);
    }

    // potion_readline_save = ^(void) {
    //     // 0:成功， -1:失败(文件不存在)。
    //     int err = linenoiseHistorySave(history_file);
    //     if (err) {
    //         FAIL_MSG("linenoiseHistorySave() return %d", err);
    //     }
    // };

    // TODO hsq 不能在 init 中调用 atexit 注册，因为 Ctrl+C 后再次启动报错 Abort trap: 6 。
    // if (atexit_b(potion_readline_save)) { // atexit(potion_readline_save)
    //     FAIL_MSG("%s", strerror(errno));
    // }

    return PN_TRUE;
}

// 存储记录。
static void potion_readline_save(void) {
    // 0:成功， -1:失败(文件不存在)。
    int err = linenoiseHistorySave(history_file);
    if (err) {
        FAIL_MSG("linenoiseHistorySave() return %d", err);
    }
}

PN_MSG(potion_readline, PN prompt) {
    char *str = linenoise(PN_STR_PTR(prompt));
    if (str == NULL) return PN_NIL;
    // TODO hsq 与前一个输入相同，为避免连续重复输入，也返回 0 。
    // assert(1 == linenoiseHistoryAdd(str));
    linenoiseHistoryAdd(str);
    // assert(0 == linenoiseHistorySave(history_file));
    potion_readline_save();
    PN code = PN_STRING(str);
    free(str);
    return code;
}